#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>

#include <util/msg.h>

#include <util/mem.h>
#include <v4l/v4l_common.h>
#include <v4l/v4l2_driver.h>

#include "v4li.h"

static void __enable_event_prop(v4lInterface * v4li, GtkWidget * widget, gboolean enable);
static void __enable_event_controls(v4lInterface * v4li, gboolean enable);
/* ========================================================================== */
static void __set_controls_default(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;
	ListIterator * it;

	it = list_get_first(v4li->l_ctrl);

	while (it) {

		struct v4l2_control ctrl;
		struct v4l2_queryctrl * qctrl = it->data;

		it = it->next;

		ctrl.id = qctrl->id;
		ctrl.value = qctrl->default_value;
		v4l2_driver_set_ctrl(fd, &ctrl);

	}

}
static void __actualize_controls(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;
	ListIterator * it;
	ListIterator * itw;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		struct v4l2_control ctrl;
		struct v4l2_queryctrl * qctrl = it->data;
		GtkWidget * wctrl = itw->data;

		it = it->next;
		itw = itw->next;


		switch (qctrl->type) {
			case V4L2_CTRL_TYPE_INTEGER:
			case V4L2_CTRL_TYPE_INTEGER64:

				// actualization ===============================================
				ctrl.id = qctrl->id;
				v4l2_driver_get_ctrl(fd, &ctrl);
				gtk_range_set_value(GTK_RANGE(wctrl), ctrl.value);
				break;

			case V4L2_CTRL_TYPE_BOOLEAN:

				// actualization ==============				=================================
				ctrl.id = qctrl->id;
				v4l2_driver_get_ctrl(fd, &ctrl);
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wctrl), ctrl.value == 1);
				break;

			case V4L2_CTRL_TYPE_MENU:

				// actualization ===============================================
				ctrl.id = qctrl->id;
				v4l2_driver_get_ctrl(fd, &ctrl);

				GtkListStore * lstore = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(wctrl)));
				GtkTreeIter iter;

				int value;
				if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(lstore), &iter)) break;

				do {

					gtk_tree_model_get(GTK_TREE_MODEL(lstore), &iter, 2, &value, -1);
					if (value == ctrl.value) {
						gtk_combo_box_set_active_iter(GTK_COMBO_BOX(wctrl), &iter);
						break;
					}

				} while (gtk_tree_model_iter_next(GTK_TREE_MODEL(lstore), &iter));


				break;

			case V4L2_CTRL_TYPE_BUTTON:
			case V4L2_CTRL_TYPE_STRING:
				break;

		}


	}

}
void event_range(GtkRange * widget, gpointer user_data) {

	v4lInterface * v4li = V4L_INTERFACE(user_data);
	ListIterator * it;
	ListIterator * itw;

	struct v4l2_queryctrl * qctrl = NULL;
	GtkWidget * wctrl = NULL;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		qctrl = it->data;
		wctrl = itw->data;
		it = it->next;
		itw = itw->next;

		if (wctrl == GTK_WIDGET(widget)) {
			break;
		}
	}

	if (qctrl == NULL) {
		msg_warning("Ooops.\n");
		return;
	}

	struct v4l2_control ctrl;
	ctrl.id = qctrl->id;
	ctrl.value = gtk_range_get_value(GTK_RANGE(widget));
	int r = v4l2_driver_set_ctrl(v4li->v4ldrv.fd, &ctrl);

	if (!r) {
		msg_warning("Can't set %s control.\n", qctrl->name);
		__actualize_controls(v4li);
	}

}
void event_combo(GtkComboBox * widget, gpointer user_data) {

	v4lInterface * v4li = V4L_INTERFACE(user_data);

	ListIterator * it;
	ListIterator * itw;

	struct v4l2_queryctrl * qctrl = NULL;
	GtkWidget * wctrl = NULL;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		qctrl = it->data;
		wctrl = itw->data;
		it = it->next;
		itw = itw->next;

		if (wctrl == GTK_WIDGET(widget)) {
			break;
		}
	}

	if (qctrl == NULL) {
		msg_warning("Ooops.\n");
		return;
	}

	struct v4l2_control ctrl;
	GtkListStore * lstore = GTK_LIST_STORE(gtk_combo_box_get_model(widget));
	GtkTreeIter iter;

	gtk_combo_box_get_active_iter(widget, &iter);
	gtk_tree_model_get(GTK_TREE_MODEL(lstore), &iter, 1, &ctrl.id, 2, &ctrl.value, -1);
	int r = v4l2_driver_set_ctrl(v4li->v4ldrv.fd, &ctrl);

	if (!r) {
		msg_warning("Can't set %s control.\n", qctrl->name);
	}

	__actualize_controls(v4li);
}
void event_check(GtkToggleButton * widget, gpointer user_data) {


	v4lInterface * v4li = V4L_INTERFACE(user_data);

	ListIterator * it;
	ListIterator * itw;

	struct v4l2_queryctrl * qctrl = NULL;
	GtkWidget * wctrl = NULL;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		qctrl = it->data;
		wctrl = itw->data;
		it = it->next;
		itw = itw->next;

		if (wctrl == GTK_WIDGET(widget)) {
			break;
		}
	}

	if (qctrl == NULL) {
		msg_warning("Ooops.\n");
		return;
	}

	struct v4l2_control ctrl;
	ctrl.id = qctrl->id;
	ctrl.value = gtk_toggle_button_get_active(widget);
	int r = v4l2_driver_set_ctrl(v4li->v4ldrv.fd, &ctrl);

	if (!r) {
		msg_warning("Can't set %s control.\n", qctrl->name);
	}
	__actualize_controls(v4li);

}
void event_button(GtkButton * widget, gpointer user_data) {


	v4lInterface * v4li = V4L_INTERFACE(user_data);

	ListIterator * it;
	ListIterator * itw;

	struct v4l2_queryctrl * qctrl = NULL;
	GtkWidget * wctrl = NULL;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		qctrl = it->data;
		wctrl = itw->data;
		it = it->next;
		itw = itw->next;

		if (wctrl == GTK_WIDGET(widget)) {
			break;
		}
	}

	if (qctrl == NULL) {
		msg_warning("Ooops.\n");
		return;
	}

	//struct v4l2_queryctrl * qctrll = user_data;
	struct v4l2_control ctrl;

	ctrl.id = qctrl->id;
	ctrl.value = 0;

	int r = v4l2_driver_set_ctrl(v4li->v4ldrv.fd, &ctrl);

	if (!r) {
		msg_warning("Can't set %s control.\n", qctrl->name);
	}

	__actualize_controls(v4li);
}
static void __make_dev_controls(v4lInterface * v4li) {

	// free memory =============================================================
	GtkWidget * page = gtk_notebook_get_nth_page(v4li->notebook, 1);
	while (page) {

		GList * childrens;
		childrens = gtk_container_get_children(GTK_CONTAINER(page));

		// remove all childrens of vbox_control
		while (childrens) {
			gtk_widget_destroy(GTK_WIDGET(childrens->data));
			childrens = childrens->next;
		}

		g_list_free(childrens);
		gtk_notebook_remove_page(v4li->notebook, 1);
		page = gtk_notebook_get_nth_page(v4li->notebook, 1);
	}

	if (v4li->l_ctrl) {
		list_remove_all_data_full(v4li->l_ctrl, free);
		list_delete(v4li->l_ctrl);
	}
	if (v4li->l_ctrl_widgets) {
		list_remove_all_data(v4li->l_ctrl_widgets);
		list_delete(v4li->l_ctrl_widgets);
	}


	// create new controls =====================================================
	int fd = v4li->v4ldrv.fd;
	v4li->l_ctrl = v4l2_driver_get_ctrl_list(fd);

	if (v4li->l_ctrl == NULL) return;


	v4li->l_ctrl_widgets = list_new();

	GtkLabel * label;
	GtkTable * table = NULL;
	int ROWS = 4;
	int COLS = 2;
	int r, c;
	int tab_count = 0;
	int count = 0;

	ListIterator * it;
	it = list_get_first(v4li->l_ctrl);
	while (it) {

		struct v4l2_queryctrl * qctrl = it->data;
		GtkWidget * wctrl = NULL;
		struct v4l2_control ctrl;

		if (count % (ROWS * COLS) == 0) {
			r = 0;
			c = 0;
			count = 0;
			table = GTK_TABLE(gtk_table_new(ROWS, COLS * 2, TRUE));

			char tab_name[12];
			sprintf(tab_name, "Controls %d", tab_count++);
			label = GTK_LABEL(gtk_label_new((const char *) tab_name));
			gtk_notebook_append_page(v4li->notebook, GTK_WIDGET(table), GTK_WIDGET(label));
		}

		r = count / COLS;
		c = count % COLS;

		qctrl->flags = fd;

		switch (qctrl->type) {
			case V4L2_CTRL_TYPE_INTEGER:
			case V4L2_CTRL_TYPE_INTEGER64:

				wctrl = gtk_hscale_new_with_range(qctrl->minimum, qctrl->maximum, qctrl->step);
				gtk_scale_set_value_pos(GTK_SCALE(wctrl), GTK_POS_RIGHT);

				break;

			case V4L2_CTRL_TYPE_BOOLEAN:
				wctrl = gtk_check_button_new_with_label(qctrl->name);

				break;

			case V4L2_CTRL_TYPE_MENU:

				wctrl = NULL;

				GtkListStore * lstore = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
				GtkCellRenderer * cellrender = gtk_cell_renderer_text_new();
				GtkTreeIter iter;

				wctrl = gtk_combo_box_new_with_model(GTK_TREE_MODEL(lstore));

				gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(wctrl), cellrender, TRUE); // Pack it to the combo box.
				gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(wctrl), cellrender, "text", 0, NULL); // Connect renderer to data source


				ctrl.id = qctrl->id;
				v4l2_driver_get_ctrl(fd, &ctrl);

				int i;
				for (i = qctrl->minimum; i <= qctrl->maximum; i++) {

					struct v4l2_querymenu qm;

					qm.id = qctrl->id;
					qm.index = i;

					if (!v4l2_driver_querymenu(fd, &qm)) continue;

					gtk_list_store_append(lstore, &iter);
					gtk_list_store_set(lstore, &iter, 0, qm.name, 1, qm.id, 2, qm.index, -1);
				}

				break;

			case V4L2_CTRL_TYPE_BUTTON:
				wctrl = gtk_button_new_with_label(qctrl->name);

				break;

			case V4L2_CTRL_TYPE_STRING:
				wctrl = gtk_entry_new_with_max_length(qctrl->maximum);

		}

		// store the created widget
		list_append(v4li->l_ctrl_widgets, wctrl);
		gtk_widget_set_size_request(GTK_WIDGET(wctrl), 100, 25);

		c = c * 2;
		if (qctrl->type == V4L2_CTRL_TYPE_BOOLEAN || qctrl->type == V4L2_CTRL_TYPE_BUTTON) {

			gtk_table_attach(table, wctrl, c, c + 2, r, r + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 0);

		} else {

			label = GTK_LABEL(gtk_label_new(qctrl->name));
			gtk_widget_set_size_request(GTK_WIDGET(label), 120, 24);
			gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5);
			gtk_table_attach(table, GTK_WIDGET(label), c, c + 1, r, r + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 0);

			gtk_table_attach(table, wctrl, c + 1, c + 2, r, r + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 2, 0);
		}

		count++;

		it = it->next;

	}

	gtk_widget_show_all(GTK_WIDGET(v4li->notebook));

	//__set_controls_default(v4li);

	__actualize_controls(v4li);
	__enable_event_controls(v4li, TRUE);
}
static void __enable_event_controls(v4lInterface * v4li, gboolean enable) {

	int fd = v4li->v4ldrv.fd;
	ListIterator * it;
	ListIterator * itw;

	it = list_get_first(v4li->l_ctrl);
	itw = list_get_first(v4li->l_ctrl_widgets);

	while (it) {

		struct v4l2_queryctrl * qctrl = it->data;
		GtkWidget * wctrl = itw->data;

		it = it->next;
		itw = itw->next;


		switch (qctrl->type) {
			case V4L2_CTRL_TYPE_INTEGER:
			case V4L2_CTRL_TYPE_INTEGER64:

				if (enable) g_signal_connect(wctrl, "value-changed", (GCallback) event_range, v4li);
				else g_signal_handlers_disconnect_by_func(wctrl, (GCallback) event_range, v4li);

				break;

			case V4L2_CTRL_TYPE_BOOLEAN:

				if (enable) g_signal_connect(wctrl, "toggled", (GCallback) event_check, v4li);
				else g_signal_handlers_disconnect_by_func(wctrl, (GCallback) event_check, v4li);

				break;

			case V4L2_CTRL_TYPE_MENU:

				if (enable) g_signal_connect(wctrl, "changed", (GCallback) event_combo, v4li);
				else g_signal_handlers_disconnect_by_func(wctrl, (GCallback) event_combo, v4li);

				break;

			case V4L2_CTRL_TYPE_BUTTON:

				if (enable) g_signal_connect(wctrl, "clicked", (GCallback) event_button, v4li);
				else g_signal_handlers_disconnect_by_func(wctrl, (GCallback) event_button, v4li);

				break;

			case V4L2_CTRL_TYPE_STRING:
				break;

		}


	}



}
/* ========================================================================== */

static void __enable_widgets_prop(v4lInterface * v4li, gboolean enable) {
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->cb_device), enable);
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->cb_v4l2_input), enable);
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->cb_v4l2_standard), enable);
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->cb_v4l2_format), enable);
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->cb_v4l2_frmsize), enable);

	gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_width), enable);
	gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_height), enable);

}
void __update_input(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;

	list_remove_all_data_full(v4li->l_v4l2_input, free);

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_input), FALSE);
	gtk_list_store_clear(v4li->ls_v4l2_input);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_input), TRUE);

	struct v4l2_input curr_input;
	if (!v4l2_driver_get_input(fd, &curr_input)) return;

	//v4li->l_v4l2_input = v4l2_driver_get_input_list(v4li->vd->fd);
	v4li->l_v4l2_input = v4l2_driver_get_input_list(fd);

	ListIterator * it;
	GtkTreeIter iter;
	char * str;
	int index = -1;

	// = v4l2_input
	it = list_get_first(v4li->l_v4l2_input);
	while (it) {
		gtk_list_store_append(v4li->ls_v4l2_input, &iter);

		struct v4l2_input * input = it->data;

		str = (char *) input->name;
		gtk_list_store_set(v4li->ls_v4l2_input, &iter, 0, str, -1);

		//if (v4li->vd->conf.v4l2.input == input->index) index = input->index;

		if (curr_input.index == input->index) index = input->index;

		it = it->next;
	}
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_input), FALSE);
	gtk_combo_box_set_active(v4li->cb_v4l2_input, index);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_input), TRUE);

}
void __update_standard(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;

	list_remove_all_data_full(v4li->l_v4l2_standard, free);

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_standard), FALSE);
	gtk_list_store_clear(v4li->ls_v4l2_standard);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_standard), TRUE);

	struct v4l2_standard curr_std;
	if (!v4l2_driver_get_standard(fd, &curr_std)) return;

	//v4li->l_v4l2_standard = v4l2_driver_get_standard_list(v4li->vd->fd);
	v4li->l_v4l2_standard = v4l2_driver_get_standard_list(fd);

	ListIterator * it;
	GtkTreeIter iter;
	char * str;
	int index = -1;

	// = v4l2_standard
	it = list_get_first(v4li->l_v4l2_standard);
	while (it) {
		gtk_list_store_append(v4li->ls_v4l2_standard, &iter);

		struct v4l2_standard * std = it->data;

		str = (char *) std->name;
		gtk_list_store_set(v4li->ls_v4l2_standard, &iter, 0, str, -1);

		//if (v4li->vd->conf.v4l2.std_id == std->id) index = std->index;
		if (curr_std.id == std->id) index = std->index;

		it = it->next;
	}

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_standard), FALSE);
	gtk_combo_box_set_active(v4li->cb_v4l2_standard, index);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_standard), TRUE);
}
void __update_format(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;

	list_remove_all_data_full(v4li->l_v4l2_format, free);

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_format), FALSE);
	gtk_list_store_clear(v4li->ls_v4l2_format);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_format), TRUE);


	struct v4l2_format curr_format;
	if (!v4l2_driver_get_format(fd, &curr_format, V4L2_BUF_TYPE_VIDEO_CAPTURE)) return;

	//v4li->l_v4l2_format = v4l2_driver_get_pixel_format_list(v4li->vd->fd);
	v4li->l_v4l2_format = v4l2_driver_get_pixel_format_list(fd);


	ListIterator * it;
	GtkTreeIter iter;
	char * str;
	int index = -1;

	// = v4l2_format
	it = list_get_first(v4li->l_v4l2_format);
	while (it) {
		gtk_list_store_append(v4li->ls_v4l2_format, &iter);

		struct v4l2_fmtdesc * fmtdesc = it->data;

		str = (char *) fmtdesc->description;
		gtk_list_store_set(v4li->ls_v4l2_format, &iter, 0, str, -1);

		//if (v4li->vd->conf.v4l2.pixelformat == fmtdesc->pixelformat) index = fmtdesc->index;
		if (curr_format.fmt.pix.pixelformat == fmtdesc->pixelformat) index = fmtdesc->index;

		it = it->next;
	}

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_format), FALSE);
	gtk_combo_box_set_active(v4li->cb_v4l2_format, index);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_format), TRUE);
}
void __update_frmsize(v4lInterface * v4li) {

	int fd = v4li->v4ldrv.fd;

	list_remove_all_data_full(v4li->l_v4l2_frmsize, free);

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_frmsize), FALSE);
	gtk_list_store_clear(v4li->ls_v4l2_frmsize);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_frmsize), TRUE);


	struct v4l2_format curr_format;
	if (!v4l2_driver_get_format(fd, &curr_format, V4L2_BUF_TYPE_VIDEO_CAPTURE)) return;

	//v4li->l_v4l2_frmsize = v4l2_driver_get_frame_size_list(v4li->vd->fd);
	v4li->l_v4l2_frmsize = v4l2_driver_get_frame_size_list(fd);

	ListIterator * it;
	GtkTreeIter iter;
	int index = -1;

	// = v4l2_frmsize
	it = list_get_first(v4li->l_v4l2_frmsize);
	while (it) {
		gtk_list_store_append(v4li->ls_v4l2_frmsize, &iter);

		struct v4l2_frmsizeenum * frm;

		frm = it->data;

		char str[32];
		sprintf(str, "%d x %d", frm->discrete.width, frm->discrete.height);

		gtk_list_store_set(v4li->ls_v4l2_frmsize, &iter, 0, str, -1);

		//if (v4li->vd->conf.width == frm->discrete.width && v4li->vd->conf.height == frm->discrete.height) index = frm->index;

		if (curr_format.fmt.pix.width == frm->discrete.width && curr_format.fmt.pix.height == frm->discrete.height) index = frm->index;

		it = it->next;
	}

	struct v4l2_format fmt;
	v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	gtk_spin_button_set_value(v4li->sp_width, fmt.fmt.pix.width);
	gtk_spin_button_set_value(v4li->sp_height, fmt.fmt.pix.height);

	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_frmsize), FALSE);
	gtk_combo_box_set_active(v4li->cb_v4l2_frmsize, index);
	__enable_event_prop(v4li, GTK_WIDGET(v4li->cb_v4l2_frmsize), TRUE);


	/*
		if (v4li->l_v4l2_frmsize->size > 0) {
			gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_width), FALSE);
			gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_height), FALSE);
		} else {
			gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_width), TRUE);
			gtk_widget_set_sensitive(GTK_WIDGET(v4li->sp_height), TRUE);
		}
	 */

}
static void __actualize_props(v4lInterface * v4li) {

	/*
		list_remove_all_data_full(v4li->l_v4l2_input, free);
		list_remove_all_data_full(v4li->l_v4l2_standard, free);
		list_remove_all_data_full(v4li->l_v4l2_format, free);
		list_remove_all_data_full(v4li->l_v4l2_frmsize, free);
	 */

	__update_input(v4li);
	__update_standard(v4li);
	__update_format(v4li);
	__update_frmsize(v4li);

}
static void __clear_lists_prop(v4lInterface * v4li) {
	list_remove_all_data_full(v4li->l_v4l2_input, free);
	list_remove_all_data_full(v4li->l_v4l2_standard, free);
	list_remove_all_data_full(v4li->l_v4l2_format, free);
	list_remove_all_data_full(v4li->l_v4l2_frmsize, free);
}
static void __clear_combos_prop(v4lInterface * v4li) {
	gtk_list_store_clear(v4li->ls_v4l2_input);
	gtk_list_store_clear(v4li->ls_v4l2_standard);
	gtk_list_store_clear(v4li->ls_v4l2_format);
	gtk_list_store_clear(v4li->ls_v4l2_frmsize);
}
/* ========================================================================== */
void event_v4li_destroy(GtkWidget * vbox, gpointer user_data) {

	v4lInterface * v4li;
	v4li = V4L_INTERFACE(user_data);

	int fd = v4li->v4ldrv.fd;

	int res;

	if (fd != -1) {

		if (strcmp(gtk_button_get_label(v4li->bt_start), "stop") == 0) {
			// try unmmap

			//res = v4l2_unmmap(&v4li->v4lbuffers);
			res = v4l_stop(&v4li->v4ldrv);
			if (!res) {
				msg_warning("Can't stop video device\n");
			};
		}
		res = v4l_close(&v4li->v4ldrv);
		if (res) msg_warning("Can't close video device\n");

	}

	gtk_widget_destroy(GTK_WIDGET(v4li->bt_start));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_device));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_v4l2_format));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_v4l2_frmint));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_v4l2_frmsize));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_v4l2_input));
	gtk_widget_destroy(GTK_WIDGET(v4li->cb_v4l2_standard));

	list_remove_all_data_full(v4li->l_device, free);
	list_remove_all_data_full(v4li->l_v4l2_format, free);
	list_remove_all_data_full(v4li->l_v4l2_frmsize, free);
	list_remove_all_data_full(v4li->l_v4l2_input, free);
	list_remove_all_data_full(v4li->l_v4l2_standard, free);

	gtk_widget_destroy(GTK_WIDGET(v4li->lb_device_bus));
	gtk_widget_destroy(GTK_WIDGET(v4li->lb_device_card));
	gtk_widget_destroy(GTK_WIDGET(v4li->lb_device_driver));
	gtk_widget_destroy(GTK_WIDGET(v4li->lb_device_name));

	gtk_list_store_clear(v4li->ls_device);
	gtk_list_store_clear(v4li->ls_v4l2_format);
	gtk_list_store_clear(v4li->ls_v4l2_frmint);
	gtk_list_store_clear(v4li->ls_v4l2_frmsize);
	gtk_list_store_clear(v4li->ls_v4l2_input);
	gtk_list_store_clear(v4li->ls_v4l2_standard);

	/*
		gtk_widget_destroy( GTK_WIDGET(v4li->re_device);
		gtk_widget_destroy( GTK_WIDGET(v4li->re_v4l2_format);
		gtk_widget_destroy( GTK_WIDGET(v4li->re_v4l2_frmint);
		gtk_widget_destroy( GTK_WIDGET(v4li->re_v4l2_frmsize);
		gtk_widget_destroy( GTK_WIDGET(v4li->re_v4l2_input);
		gtk_widget_destroy( GTK_WIDGET(v4li->re_v4l2_standard);
	 */

	gtk_widget_destroy(GTK_WIDGET(v4li->sp_width));
	gtk_widget_destroy(GTK_WIDGET(v4li->sp_height));

}
void event_cb_device_changed(GtkComboBox * combobox, gpointer user_data) {

	v4lInterface * v4li;

	GtkTreeIter iter;
	GtkTreeModel *model;
	char * device = NULL;

	v4li = V4L_INTERFACE(user_data);

	if (gtk_combo_box_get_active_iter(combobox, &iter)) {

		model = gtk_combo_box_get_model(combobox);
		gtk_tree_model_get(model, &iter, 0, &device, -1);


		if (v4li->v4ldrv.fd != -1) v4l_close(&v4li->v4ldrv);

		v4li->v4ldrv = v4l_open_default(device);

		if (v4li->v4ldrv.fd < 0 ) {
			printf("Just in use\n");
			__clear_lists_prop(v4li);
			__clear_combos_prop(v4li);
			return;
		}


		/*
				if (v4li->vd != NULL) imp_video_device_close(v4li->vd);
				v4li->vd = imp_video_device_new();
				imp_video_device_set_device_path(v4li->vd, device);
				imp_video_device_open(v4li->vd);

				if (!imp_video_device_is_opened(v4li->vd)) {
					// can not open the device
					printf("Just in use\n");
					__clear_lists_prop(v4li);
					__clear_combos_prop(v4li);
					return;
				}
		 */

		struct v4l2_capability vcap;
		//v4l2_driver_get_capability(v4li->vd->fd, &vcap);
		v4l2_driver_get_capability(v4li->v4ldrv.fd, &vcap);

		gtk_label_set_text(v4li->lb_device_name, device);
		gtk_label_set_text(v4li->lb_device_card, (char *) vcap.card);
		gtk_label_set_text(v4li->lb_device_driver, (char *) vcap.driver);
		gtk_label_set_text(v4li->lb_device_bus, (char *) vcap.bus_info);

		struct v4l2_format fmt;
		//v4l2_driver_get_format(v4li->vd->fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
		v4l2_driver_get_format(v4li->v4ldrv.fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);

		gtk_spin_button_set_value(v4li->sp_width, fmt.fmt.pix.width);
		gtk_spin_button_set_value(v4li->sp_height, fmt.fmt.pix.height);

		// update capabilities
		/*
				__update_input(v4li);
				__update_standard(v4li);
				__update_format(v4li);
				__update_frmsize(v4li);
		 */

		__actualize_props(v4li);

		__make_dev_controls(v4li);

	}
}
void event_bt_start_clicked(GtkButton * button, gpointer user_data) {

	v4lInterface * v4li;

	v4li = V4L_INTERFACE(user_data);
	int fd = v4li->v4ldrv.fd;

	//if (v4li->vd == NULL) return;
	//v4li->vd->v4l_version = IMP_VIDEO_DEVICE_VERSION_V4L2;

	if (strcmp(gtk_button_get_label(button), "start") == 0) {

		if (v4li->v4ldrv.fd <= 0) return;

		struct v4l2_format fmt;
		int val = -1;

		val = v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
		if (!val) msg_warning("Cannot get format.\n"); //return;

		fmt.fmt.pix.width = gtk_spin_button_get_value_as_int(v4li->sp_width);
		fmt.fmt.pix.height = gtk_spin_button_get_value_as_int(v4li->sp_height);
		//fmt.fmt.pix.field = V4L2_FIELD_ALTERNATE;

		val = v4l2_driver_set_format(fd, &fmt);
		if (!val) msg_warning("Cannot set format.\n"); //return;

		val = v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
		if (!val) msg_warning("Cannot set format.\n"); //return;

		gtk_spin_button_set_value(v4li->sp_width, fmt.fmt.pix.width);
		gtk_spin_button_set_value(v4li->sp_height, fmt.fmt.pix.height);

		v4l_start(&v4li->v4ldrv);

		gtk_button_set_label(button, "stop");
		__enable_widgets_prop(v4li, FALSE);
		g_signal_emit_by_name(v4li, "v4li-start-event", NULL);

	} else {

		v4l_stop(&v4li->v4ldrv);

		gtk_button_set_label(button, "start");
		__enable_widgets_prop(v4li, TRUE);

		g_signal_emit_by_name(v4li, "v4li-stop-event", NULL);
	}
}
void event_cb_v4l2_input_changed(GtkComboBox * combobox, gpointer user_data) {

	v4lInterface * v4li;

	v4li = V4L_INTERFACE(user_data);
	int fd = v4li->v4ldrv.fd;

	int id = gtk_combo_box_get_active(combobox);
	struct v4l2_input * input;


	input = list_get_data_at_index(v4li->l_v4l2_input, id);


	if (input == NULL) return;

	//v4l2_driver_set_input(v4li->vd->fd, &input->index);
	v4l2_driver_set_input(fd, (int *) &input->index);

	__actualize_props(v4li);

}
void event_cb_v4l2_standard_changed(GtkComboBox * combobox, gpointer user_data) {

	v4lInterface * v4li;

	v4li = V4L_INTERFACE(user_data);
	int fd = v4li->v4ldrv.fd;

	int id = gtk_combo_box_get_active(combobox);
	struct v4l2_standard * standard;

	standard = list_get_data_at_index(v4li->l_v4l2_standard, id);

	if (standard == NULL) return;

	//v4l2_driver_set_standard_id(v4li->vd->fd, &standard->id);
	v4l2_driver_set_standard_id(fd, &standard->id);

	__actualize_props(v4li);
}
void event_cb_v4l2_format_changed(GtkComboBox * combobox, gpointer user_data) {

	v4lInterface * v4li;

	v4li = V4L_INTERFACE(user_data);
	int fd = v4li->v4ldrv.fd;

	int id = gtk_combo_box_get_active(combobox);
	struct v4l2_fmtdesc * fmtdesc;

	fmtdesc = list_get_data_at_index(v4li->l_v4l2_format, id);


	if (fmtdesc == NULL) return;

	struct v4l2_format fmt;

	v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	fmt.fmt.pix.pixelformat = fmtdesc->pixelformat;
	//fmt.fmt.pix.field = V4L2_FIELD_ALTERNATE;
	//fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
	v4l2_driver_set_format(fd, &fmt);

	//v4l2_driver_get_format(v4li->vd->fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);

	__actualize_props(v4li);

}
void event_cb_v4l2_frmsize_changed(GtkComboBox * combobox, gpointer user_data) {

	v4lInterface * v4li;

	v4li = V4L_INTERFACE(user_data);
	int fd = v4li->v4ldrv.fd;

	int id = gtk_combo_box_get_active(combobox);
	struct v4l2_frmsizeenum * frm;

	frm = list_get_data_at_index(v4li->l_v4l2_frmsize, id);

	if (frm == NULL) return;

	struct v4l2_format fmt;

	//v4l2_driver_get_format(v4li->vd->fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	v4l2_driver_get_format(fd, &fmt, V4L2_BUF_TYPE_VIDEO_CAPTURE);
	fmt.fmt.pix.width = frm->discrete.width;
	fmt.fmt.pix.height = frm->discrete.height;
	//v4l2_driver_set_format(v4li->vd->fd, &fmt);
	v4l2_driver_set_format(fd, &fmt);

	gtk_spin_button_set_value(v4li->sp_width, fmt.fmt.pix.width);
	gtk_spin_button_set_value(v4li->sp_height, fmt.fmt.pix.height);


	__actualize_props(v4li);
}
void init_interface_events(v4lInterface * v4li) {

	g_signal_connect(v4li, "destroy", (GCallback) event_v4li_destroy, v4li);

	g_signal_connect(v4li->cb_device, "changed", (GCallback) event_cb_device_changed, v4li);

	g_signal_connect(v4li->bt_start, "clicked", (GCallback) event_bt_start_clicked, v4li);

	g_signal_connect(v4li->cb_v4l2_input, "changed", (GCallback) event_cb_v4l2_input_changed, v4li);
	g_signal_connect(v4li->cb_v4l2_standard, "changed", (GCallback) event_cb_v4l2_standard_changed, v4li);
	g_signal_connect(v4li->cb_v4l2_frmsize, "changed", (GCallback) event_cb_v4l2_frmsize_changed, v4li);


}
static void __enable_event_prop(v4lInterface * v4li, GtkWidget * widget, gboolean enable) {


	if (GTK_WIDGET(v4li->cb_v4l2_input) == widget) {
		if (enable) g_signal_connect(v4li->cb_v4l2_input, "changed", (GCallback) event_cb_v4l2_input_changed, v4li);
		else g_signal_handlers_disconnect_by_func(widget, (GCallback) event_cb_v4l2_input_changed, v4li);
		return;
	}

	if (GTK_WIDGET(v4li->cb_v4l2_standard) == widget) {
		if (enable) g_signal_connect(v4li->cb_v4l2_standard, "changed", (GCallback) event_cb_v4l2_standard_changed, v4li);
		else g_signal_handlers_disconnect_by_func(widget, (GCallback) event_cb_v4l2_standard_changed, v4li);
		return;
	}

	if (GTK_WIDGET(v4li->cb_v4l2_format) == widget) {
		if (enable) g_signal_connect(v4li->cb_v4l2_format, "changed", (GCallback) event_cb_v4l2_format_changed, v4li);
		else g_signal_handlers_disconnect_by_func(widget, (GCallback) event_cb_v4l2_format_changed, v4li);
		return;
	}

	if (GTK_WIDGET(v4li->cb_v4l2_frmsize) == widget) {
		if (enable) g_signal_connect(v4li->cb_v4l2_frmsize, "changed", (GCallback) event_cb_v4l2_frmsize_changed, v4li);
		else g_signal_handlers_disconnect_by_func(widget, (GCallback) event_cb_v4l2_frmsize_changed, v4li);
		return;
	}

}
/* ========================================================================== */


